package Coriander::Window::AddressBook;
use strict;
use warnings;
use Storable 'dclone';
our @ISA = qw(Coriander::Window::Toplevel);


my @world = ();
my $selectedWorld = undef;
my $selectedCharacterName = undef;

# Called by Coriander::Window's _init method.
sub windowLoad {
    my $win = shift;
    my @pad5 = (qw(-padx 5 -pady 5));
    my @caption = (-font => "system-bold", -background => "#808080", -foreground => "#ffffff", @pad5);
    my @buttonWidth = (qw(-width 10));
    
    my $padding = 5;
    
    $win->title("Address book");

    
    # Worlds
    
    # Just a container, holding controls that deal with worlds.
    my $frameWorlds = $win->LabFrame(
        Name            => "frameWorlds",
        -label          => "Worlds",
        @pad5,
    )->form(-top => $padding, -bottom => ["%100", $padding * -1], -left => $padding, -right => "%33");
    
    # Listbox that holds the worlds.
    my $listWorlds = $frameWorlds->Scrolled("Listbox",
        Name            => "listWorlds",
        -scrollbars     => "oe",
        -selectmode     => "single",
        -exportselection=> 0,
    );    
    
    # Button to create a new world
    my $butAddWorld = $frameWorlds->Button(
        Name            => "butAddWorld",
        -text           => "Add",
        @buttonWidth,
        @pad5,
    );
    
    # Button to delete the currently selected world
    my $butDelWorld = $frameWorlds->Button(
        Name            => "butDelWorld",
        -text           => "Delete",
        -state          => "disabled",
        @buttonWidth,
        @pad5,
    );
    
    # "Fancy" label
    my $labWorlds = $frameWorlds->Label(Name => "labWorlds", -text => "Worlds", @caption)->form(-top => 0, -left => 0, -right => "%100");
    
    # Put it all in place.
    $listWorlds->form(-top => [$labWorlds, $padding], -bottom => [$butAddWorld, $padding * -1], -left => 0, -right => "%100");
    $butAddWorld->form(-bottom => "%100", -left=> 0);
    $butDelWorld->form(-bottom => "%100", -left => [$butAddWorld, $padding]);
    
    
    
    # Quick Connect
    
    # Container that allows you to Quick Connect to a world.
    my $frameQuickConnect = $win->LabFrame(
        Name            => "frameQuickConnect",
        -label          => "Quick connect",
        @pad5
    )->form(-top => $padding, -left => [$frameWorlds, $padding], -right => ["%100", $padding * -1]);
    
    # Labels
    my $labQuickHost = $frameQuickConnect->Label(Name => "labQuickHost", -text => "Host",  -anchor => "e");
    my $labQuickPort = $frameQuickConnect->Label(Name => "labQuickPort", -text => "Port",  -anchor => "e");
    
    # Input boxes for host name and port.
    my $textQuickHost = $frameQuickConnect->Entry(
        Name            => "textQuickHost",
    );
    my $textQuickPort = $frameQuickConnect->Entry(
        Name            => "textQuickPort",
        -width          => 10,
    );
    my $butQuickConnect = $frameQuickConnect->Button(
        Name            => "butQuickConnect",
        -text           => "Connect",
        @buttonWidth,
    );

    # Place it.
    $labQuickHost->form(-top => 0, -left => 0);
    $textQuickHost->form(-top => 0, -left => $labQuickHost, -right => $labQuickPort);
    
    $labQuickPort->form(-top => 0, -right => $textQuickPort);
    $textQuickPort->form(-top => 0, -right => [$butQuickConnect, $padding * -1]);
    
    $butQuickConnect->form(-top => 0, -right => "%100");

    
    # World Date.

    # Container for the controls that allow you to view and edit the selected world.
    my $frameWorld = $win->LabFrame(
        Name            => "frameWorld",
        -label          => "World data",
        @pad5
    )->form(-top => [$frameQuickConnect, $padding], -left => [$frameWorlds, $padding], -right => ["%100", $padding * -1] );
    
    # Labels.
    my $labWorldName = $frameWorld->Label(Name => "labWorldName", -text => "World name", -anchor => "e");
    my $labWorldHost = $frameWorld->Label(Name => "labWorldHost", -text => "Host",  -anchor => "e");
    my $labWorldPort = $frameWorld->Label(Name => "labWorldPort", -text => "Port",  -anchor => "e");

    # Input boxes for the world's name, host, and port.
    # Disabled by default.
    my $textWorldName = $frameWorld->Entry(
        Name            => "textWorldName",
        -state          => "readonly",
    );
    my $textWorldHost = $frameWorld->Entry(
        Name            => "textWorldHost",
        -state          => "readonly",
    );
    my $textWorldPort = $frameWorld->Entry(
        Name            => "textWorldPort",
        -state          => "readonly",
        -width          => 10,
    );
    
    # Button that lets you edit the world data.
    my $butEditWorld = $frameWorld->Button(
        Name            => "butEditWorld",
        -text           => "Edit",
        @buttonWidth,
        @pad5,
    );
    
    # Button that lets you save the world data after editting.
    my $butSaveWorld = $frameWorld->Button(
        Name            => "butSaveWorld",
        -text           => "Save",
        -state          => "disabled",
        @buttonWidth,
        @pad5,
    );
    
    # Button to make an identical copy of the world.
    my $butCopyWorld = $frameWorld->Button(
        Name            => "butCopyWorld",
        -text           => "Copy",
        @buttonWidth,
        @pad5,
    );
    
    # "Fancy" label.
    my $labWorld = $frameWorld->Label(Name => "labWorld", -text => "World", @caption)->form(-top => 0, -left => 0, -right => "%100");
    
    # And actually map it all again.
    $labWorldName->form(-top => [$labWorld, $padding], -left => 0);
    $textWorldName->form(-top => ["&", $labWorldName], -left => $labWorldName, -right => "%100");
    
    $labWorldHost->form(-top => [$labWorldName, $padding], -left => 0);
    $textWorldHost->form(-top => ["&", $labWorldHost], -left => ["&", $textWorldName], -right => $labWorldPort);
    
    $labWorldPort->form(-top => ["&", $labWorldHost], -right => $textWorldPort);
    $textWorldPort->form(-top => ["&", $labWorldHost], -right => "%100");

    $butEditWorld->form(-top => [$labWorldPort, $padding], -bottom => "%100", -left => 0);
    $butSaveWorld->form(-top => ["&", $butEditWorld], -bottom => "%100", -left => [$butEditWorld, $padding]);
    $butCopyWorld->form(-top => ["&", $butEditWorld], -bottom => "%100", -left => [$butSaveWorld, $padding]);

    
    # Characters
    
    # Container for controls that deal with this world's characters   
    my $frameCharacters = $win->LabFrame(
        Name            => "frameCharacters",
        -label          => "Characters",
        @pad5,
    )->form(-top => [$frameWorld, $padding], -bottom => ["&", $frameWorld], -left => ["&", $frameWorld], -right => "%66");

    # "Fancy" label again.
    my $labCharacters = $frameCharacters->Label(Name => "labCharacters", -text => "Characters", @caption);
    
    # Listbox that will hold the names of the characters of this world.
    my $listCharacters = $frameCharacters->Scrolled("Listbox",
        Name            => "listCharacters",
        -scrollbars     => "oe",
        -selectmode     => "single",
        -exportselection=> 0,
    );
    
    # Button that creates a new character.
    my $butAddCharacter = $frameCharacters->Button(
        Name            => "butAddCharacter",
        -text           => "Add",
        @buttonWidth,
        @pad5,
    );
    
    # Button to delete a character.
    my $butDelCharacter = $frameCharacters->Button(
        Name            => "butDelCharacter",
        -text           => "Delete",
        -state          => "disabled",
        @buttonWidth,
        @pad5,
    );
        
    # And hand it to the geometry manager.
    $labCharacters->form(-top => 0, -left => 0, -right => "%100");
    $listCharacters->form(-top => [$labCharacters, $padding], -bottom => [$butAddCharacter, $padding * -1], -left => 0, -right => "%100");

    $butAddCharacter->form(-bottom => "%100", -left => 0);
    $butDelCharacter->form(-bottom => "%100", -left => [$butAddCharacter, $padding]);
    
    
    # Character
    
    # Container for controls that let you see and edit a character.
    my $frameCharacter = $win->LabFrame(
        Name            => "frameCharacter",
        -label          => "Character data",
        @pad5,
    )->form(-top => ["&", $frameCharacters], -bottom => ["&", $frameCharacters], -left => [$frameCharacters, $padding], -right => ["%100", $padding * -1]);
    
    # Labels.
    my $labCharacter = $frameCharacter->Label(Name => "labCharacter", -text => "Character", @caption);
    my $labCharacterName = $frameCharacter->Label(Name => "labCharacterName", -text => "Character name", -anchor => "e");
    my $labCharacterPassword = $frameCharacter->Label(Name => "labCharacterPassword", -text => "Password", -anchor => "e");
    
    # Input boxes for the character's name and password
    my $textCharacterName = $frameCharacter->Entry(
        Name            =>  "textCharacterName",
        -state          =>  "readonly"
    );
    my $textCharacterPassword = $frameCharacter->Entry(
        Name            =>  "textCharacterPassword",
        -state          =>  "readonly",
        -show           =>  "*"
    );
    
    # Checkboxes that allow you to mark characters as auto-connect or favourite.
    my $checkCharacterAutoConnect = $frameCharacter->Checkbutton(
        Name            =>  "checkCharacterAutoConnnect",
        -text           =>  "Auto connect",
        -anchor         => "w",
        -state          => "disabled"
    );
    my $checkCharacterFavourite = $frameCharacter->Checkbutton(
        Name            =>  "checkCharacterFavourite",
        -text           =>  "Favourite",
        -anchor         => "w",
        -state          => "disabled"
    );

    # Button that enabled the input boxes and check boxes so you can edit
    # the character.
    my $butEditCharacter = $frameCharacter->Button(
        Name            => "butEditCharacter",
        -text           => "Edit",
        -state          => "disabled",
        @buttonWidth,
        @pad5,
    );
    
    # Button to save the character after editting.
    my $butSaveCharacter = $frameCharacter->Button(
        Name            => "butSaveCharacter",
        -text           => "Save",
        -state          => "disabled",
        @buttonWidth,
        @pad5
    );
    
    # Button to connect to this character right away.
    my $butConnectCharacter = $frameCharacter->Button(
        Name            => "butConnectCharacter",
        -text           => "Connect",
        @buttonWidth,
        @pad5
    );
    
    # And put it on the window.
    $labCharacter->form(-top => 0, -left => 0, -right => "%100");
    $labCharacterName->form(-top => [$labCharacter, $padding], -left => 0);
    $textCharacterName->form(-top => ["&", $labCharacterName], -left => $labCharacterName, -right => "%100");

    $labCharacterPassword->form(-top => [$labCharacterName, $padding], -left => 0);
    $textCharacterPassword->form(-top => ["&", $labCharacterPassword], -left => ["&", $textCharacterName], -right => "%100");
    
    $checkCharacterAutoConnect->form(-top => [$labCharacterPassword, $padding], -left => 0);
    
    $butEditCharacter->form(-bottom => "%100", -left => 0);
    $butSaveCharacter->form(-bottom => "%100", -left => [$butEditCharacter, $padding]);
    $butConnectCharacter->form(-bottom => "%100", -left => [$butSaveCharacter, $padding]);
    
    # Call listWorlds_Select when the uses selects an element from the worlds listbox.
    # @_ contains the listbox, so let's just pass that to our handler too.
    $listWorlds->bind("<<ListboxSelect>>", sub { $win->listWorlds_Select(@_) });
    
    # Courtesy of Coriander::Window. Set handlers for button clicks.
    $win->buttonHandlers;
}


# Called by Coriander::Window's _init method.
# Nothing fancy happens here.
# We load the worlds, and hide the controls we don't immediately need
# so that the address book doesn't look as intimidating.
# By exposing the user to the various components on an as-needed basis
# the sheer amount of controls on this window is less overwhelming.
# Arguably this means that the window is poorly designed, but then again
# I don't feel like spreading out functionality that is obviously so closely related.
sub windowShow {
    my $win = shift;
    @world = Coriander::World::loadAll();
    $win->hideFrameWorld;
    
    my $listWorlds = $win->localWidget("listWorlds");
    
    for my $w (@world) {
        $listWorlds->insert('end', $w->get('name'));
    }
}        
        
#
# Event handlers for the buttons follow
#

# Event handler: add a world
sub butAddWorld_Click {
    my $win = shift;
    
    # Hide irrelevant controls.
    $win->hideFrameWorld;   
    
    # Create the new world object
    push @world, Coriander::World->new();
    
    # Push it into the list, and select it
    my $worlds = $win->localWidget("listWorlds");
    $worlds->insert('end', $world[-1]->get('name'));
    $worlds->see('end');
    $worlds->selectionClear(0, 'end');
    $worlds->selectionSet('end');
    $win->listWorlds_Select($worlds);
    $win->butEditWorld_Click;
}

# Event handler: delete a world
sub butDelWorld_Click {
    my $win = shift;
    my $worlds = $win->localWidget("listWorlds");
    my ($idx) = $worlds->curselection;
    
    # Are you sure? 
    my $answer = $win->messageBox(
        -title => 'Delete world?',
        -message => sprintf("Permanently delete this world?\n%s\n%s %s", $world[$idx]->get("name"), $world[$idx]->get("host"), $world[$idx]->get("port")), 
        -type => 'YesNo', -icon => 'warning', -default => 'no'
    );
    
    # - Why yes, I am sure.
    if ($answer eq "Yes") {
        # Hide irrlevant controls.
        $win->hideFrameWorld;
        
        # Remove the world from the GUI...
        $win->localWidget("butDelWorld")->configure(-state => "disabled");
        $worlds->delete($idx);
        $worlds->selectionClear(0,'end');
        
        # ... from the hard disk...
        $world[$idx]->delete;
        
        # ... and from our internal list of worlds.
        splice @world, $idx, 1;
    }
}

# Event handler: quick connect
# TODO.
sub butQuickConnect_Click {
    my $win = shift;
}

# Event handler: edit a world
sub butEditWorld_Click {
    my $win = shift;
    
    # Activate the relevant controls
    my ($name, $host, $port, $save) = $win->localWidgets(qw(textWorldName textWorldHost textWorldPort butSaveWorld));
    for my $w ($name,$host,$port,$save) {
        $w->configure(-state => "normal");
    }
    
    # Make it so that the user can begin bashing at their keyboard!
    $name->focus;
    $name->icursor('end');
    $name->selectionRange('0', 'end');
    
}

# Event handler: save the world
# Mal: What does that make us?
# Zoe: Big Damn Heroes, sir.
# Mal: Ain't we just.
sub butSaveWorld_Click {
    my $win = shift;
    
    # Disable the controls
    my ($name, $host, $port, $save) = $win->localWidgets(qw(textWorldName textWorldHost textWorldPort butSaveWorld));
    for my $w ($name,$host,$port,$save) {
        $w->configure(-state => "disabled");
    }
    
    # Copy the new world data into the world object.
    my $worlds = $win->localWidget("listWorlds");
    my ($idx) = $worlds->curselection;
    $world[$idx]->flush(0);
    $world[$idx]->set('name', $name->get);
    $world[$idx]->set('host', $host->get);
    $world[$idx]->set('port', $port->get);
    $world[$idx]->save;
    
    # Update the list box.
    $worlds->delete($idx);
    $worlds->insert($idx, $name->get);
    $worlds->selectionClear(0,'end');
    $worlds->selectionSet($idx);
    $win->listWorlds_Select($worlds);
}

# Event handler: copy a world
sub butCopyWorld_Click {
    my $win = shift;
    my $worlds = $win->localWidget("listWorlds");
    my ($idx) = $worlds->curselection;
    
    # Create a brand new world object...
    push @world, Coriander::World->new();
    
    # ... and deepclone the source world into it.
    %{$world[-1]} = %{dclone $world[$idx]};
    
    # Give it an original name and save it.
    $world[-1]->set('name', "Copy of " . $world[-1]->get('name'));
    $world[-1]->save;
    
    # Put the copy in the listbox and select it.
    $worlds->insert($idx+1, $world[-1]->get('name'));
    $worlds->selectionClear(0,'end');
    $worlds->selectionSet($idx+1);
    $win->listWorlds_Select($worlds);
}

# Event handler: create a new character
# TODO
sub butAddCharacter_Click {
    my $win = shift;
}

# Event handler: delete a character
# TODO
sub butDelCharacter_Click {
    my $win = shift;
}

# Event handler: edit a character
# TODO
sub butEditCharacter_Click {
    my $win = shift;
}

# Event handler: save the character
# TODO
sub butSaveCharacter_Click {
    my $win = shift;
}

# Event handler: a world gets selected from the listbox.
sub listWorlds_Select {
    my ($win, $worlds) = @_;
    my ($idx) = ($worlds->curselection);
    return if not defined $idx;
    my ($qhost, $qport, $name, $host, $port, $delete, $edit) = $win->localWidgets(qw(textQuickHost textQuickPort textWorldName textWorldHost textWorldPort butDelWorld butEditWorld));

    for my $widget ($qhost,$qport,$name,$host,$port,$delete,$edit) {
        if ($widget->Class eq "Entry") { $widget->delete('0', 'end'); }
        if ($widget->Class eq "Button") { $widget->configure(-state => "normal"); }
    }

    $qhost->insert('end', $world[$idx]->get('host'));
    $qport->insert('end', $world[$idx]->get('port'));
    
    $win->localWidget("labWorld")->configure(-text => $world[$idx]->get('name'));

    $name->configure(-textvariable => \$world[$idx]->{name});
    $name->configure(-textvariable => undef);
    $host->configure(-textvariable => \$world[$idx]->{host});
    $host->configure(-textvariable => undef);
    $port->configure(-textvariable => \$world[$idx]->{port});
    $port->configure(-textvariable => undef);
    
    my ($characters, $addchar) = $win->localWidgets(qw(listCharacters));
    $characters->delete('0', 'end');

    for my $char ( sort keys %{$world[$idx]->get('characters')} ) {
        $characters->insert('end', $char);
    }    
    
    $win->showFrameWorld;
    $win->showFrameCharacters;
}

#
# A bunch of subroutines for our convenience.
#

# Show the frameWorld frame (i.e. the control that lets you see and edit world data)
sub showFrameWorld {
    my $win = shift;
    my ($frameWorlds, $frameQuickConnect, $frameWorld ) = $win->localWidgets("frameWorlds", "frameQuickConnect", "frameWorld");
    # Put controls in their default state
    $_->configure(-state => "disabled") for $win->localWidgets("textWorldName", "textWorldHost", "textWorldPort", "butSaveWorld");
    
    # Hide character-related controls.
    $win->hideFrameCharacters;
    
    # If frameWorld isn't already visible, make it so.
    return if $frameWorld->manager;
    $frameWorld->form(-top => [$frameQuickConnect, 5], -left => ["&", $frameQuickConnect], -right => ["&", $frameQuickConnect]);
}

# Hide the frameWorld frame.
sub hideFrameWorld {
    my $win = shift;
    my $frameWorld = $win->localWidget("frameWorld");
    return unless $frameWorld->manager;
    $win->hideFrameCharacters;
    $frameWorld->formForget;
}

# Show/hide the frame that holds the list of characters
# and also the fram that lets you see and edit a single character.
sub showFrameCharacters {
    my $win = shift;
    my ($frameWorld, $frameCharacters) = $win->localWidgets("frameWorld", "frameCharacters");
    $_->configure(-state => "disabled") for $win->localWidgets("butDelCharacter");
    $win->hideFrameCharacter;
    return if $frameCharacters->manager;
    $frameCharacters->form(-top => [$frameWorld, 5], -bottom => ["%100", -5], -left => ["&", $frameWorld], -right => "%66");
}
sub hideFrameCharacters {
    my $win = shift;
    my $frameCharacters = $win->localWidget("frameCharacters");
    return unless $frameCharacters->manager;
    $win->hideFrameCharacter;
    $frameCharacters->formForget;
}

# Show/hide the frame that lets you see, edit, and connect to a character.
sub showFrameCharacter {
    my $win = shift;
    my ($frameWorld, $frameCharacters, $frameCharacter) = $win->localWidgets("frameWorld", "frameCharacters". "frameCharacter");
    $_->configure(-state => "disabled") for $win->localidgets("textCharacterName", "textCharacterPassword", "checkCharacterAutoConnect", "checkCharacterFavourite", "butCharacterEdit", "butCharacterSave");
    return if $frameCharacter->manager;
    $frameCharacters->form(-top => [$frameWorld, 5], -bottom => ["%100", -5], -left => [$frameCharacters, 5], -right => ["&", $frameWorld]);
}
sub hideFrameCharacter {
    my $win = shift;
    my $frameCharacter = $win->localWidget("frameCharacter");
    return unless $frameCharacter->manager;
    $frameCharacter->formForget;
}

1;